package com.jiunuan.live.user.provider.service.impl;

import com.alibaba.fastjson.JSON;
import com.jiunuan.live.common.interfaces.topic.UserProviderTopicNames;
import com.jiunuan.live.common.interfaces.utils.ConvertBeanUtils;
import com.jiunuan.live.framework.redis.starter.key.UserProviderCacheKeyBuilder;
import com.jiunuan.live.user.constants.CacheAsyncDeleteCode;
import com.jiunuan.live.user.constants.UserTagFieldNameConstants;
import com.jiunuan.live.user.constants.UserTagsEnum;
import com.jiunuan.live.user.dto.UserCacheAsyncDeleteDTO;
import com.jiunuan.live.user.dto.UserTagDTO;
import com.jiunuan.live.user.provider.dao.mapper.IUserTagMapper;
import com.jiunuan.live.user.provider.dao.po.UserTagPO;
import com.jiunuan.live.user.provider.service.IUserTagService;
import com.jiunuan.live.user.utils.TagInfoUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.MQProducer;
import org.apache.rocketmq.common.message.Message;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author: jiunuan
 * @date: 2024/8/2
 */
@Service
@Slf4j
public class UserTagServiceImpl implements IUserTagService {
    @Resource
    private IUserTagMapper userTagMapper;
    @Resource
    private RedisTemplate<String, UserTagDTO> redisTemplate;
    @Resource
    private UserProviderCacheKeyBuilder cacheKeyBuilder;
    @Resource
    private MQProducer mqProducer;
    @Override
    public boolean setTag(Long userId, UserTagsEnum userTagsEnum) {
        /*
            1. 尝试update true return
            2. 设置了标签没有记录（2种失败场景）
            3. select is null,insert return ,update
         */
        boolean updateStatus = userTagMapper.setTag(userId,userTagsEnum.getFieldName(), userTagsEnum.getTag()) > 0;
        if(updateStatus){
            return true;
        }
        /*
            更新失败查询核对
            redis分担多线程并发，userId主键重复，插入失败问题
         */
        String setNxKey = cacheKeyBuilder.buildTagLockKey(userId);
        String setNxResult = redisTemplate.execute(new RedisCallback<String>() {
            /**
             * 在Redis中执行设置操作。
             *
             * @param connection Redis连接
             * @return 设置操作的结果
             * @throws DataAccessException 数据访问异常
             */
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                // 获取Redis模板的键和值序列化器
                RedisSerializer keySerializer = redisTemplate.getKeySerializer();
                RedisSerializer valueSerializer = redisTemplate.getValueSerializer();

                // 使用Redis的SET命令设置键值对，"-1"作为值表示占用锁，NX表示仅当键不存在时设置，EX表示设置键的过期时间
                // NX（Not eXist）确保只有在键不存在时才设置，从而实现锁的逻辑；EX（eXpire）设置锁的过期时间，防止死锁
                return (String) connection.execute("set",keySerializer.serialize(setNxKey),
                        valueSerializer.serialize("-1"),
                        "NX".getBytes(StandardCharsets.UTF_8),
                        "EX".getBytes(StandardCharsets.UTF_8),
                        "3".getBytes(StandardCharsets.UTF_8));
            }
        });
        if(!"OK".equals(setNxResult)){
            // 设置失败，说明其他线程已经设置过，返回false
            return false;
        }

        UserTagPO userTagPO = userTagMapper.selectById(userId);
        if(userTagPO != null){
            // 不为空，说明设置重复，返回false
            return false;
        }
        userTagPO = new UserTagPO();
        userTagPO.setUserId(userId);
        userTagMapper.insert(userTagPO);
        updateStatus = userTagMapper.setTag(userId,userTagsEnum.getFieldName(), userTagsEnum.getTag()) > 0;
        log.info("测试更新成功");
        //设置成功，删除NxKey
        redisTemplate.delete(setNxKey);
        return updateStatus;
    }

    @Override
    public boolean cancelTag(Long userId, UserTagsEnum userTagsEnum) {
        boolean cancelStatus = userTagMapper.cancelTag(userId, userTagsEnum.getFieldName(), userTagsEnum.getTag()) > 0;
        //如果失败
        if(!cancelStatus){
            return false;
        }
        deleteUserTagDTOFromRedis(userId);
        return true;
    }

    @Override
    public boolean containTag(Long userId, UserTagsEnum userTagsEnum) {
        UserTagDTO userTagDTO = this.queryByUserIdFromRedis(userId);
        if(userTagDTO == null){
            return false;
        }
        String queryFieldName = userTagsEnum.getFieldName();
        //需要根据标签枚举中的fieldName来识别需要匹配MySQL表中哪个字段的标签值
        /*
            todo 需要考虑标签值过多的情况
         */
        if(UserTagFieldNameConstants.TAG_INFO_01.equals(queryFieldName)){
            return TagInfoUtils.isContain(userTagDTO.getTagInfo01(), userTagsEnum.getTag());
        }else if(UserTagFieldNameConstants.TAG_INFO_02.equals(queryFieldName)){
            return TagInfoUtils.isContain(userTagDTO.getTagInfo02(), userTagsEnum.getTag());
        }else if (UserTagFieldNameConstants.TAG_INFO_03.equals(queryFieldName)){
            return TagInfoUtils.isContain(userTagDTO.getTagInfo03(), userTagsEnum.getTag());
        }
        return false;
    }

    /**
     * 从redis缓存删除用户标签
     * @param userId 用户id
     */
    private void deleteUserTagDTOFromRedis(Long userId){
        String redisKey = cacheKeyBuilder.buildTagKey(userId);
        redisTemplate.delete(redisKey);

        UserCacheAsyncDeleteDTO userCacheAsyncDeleteDTO = new UserCacheAsyncDeleteDTO();
        userCacheAsyncDeleteDTO.setCode(CacheAsyncDeleteCode.USER_TAG_DELETE.getCode());
        Map<String, Object> jsonParam = new HashMap<>();
        jsonParam.put("userId",userId);
        userCacheAsyncDeleteDTO.setJson(JSON.toJSONString(jsonParam));

        Message message = new Message();
        message.setTopic(UserProviderTopicNames.CACHE_ASYNC_DELETE_TOPIC);
        message.setBody(JSON.toJSONString(userCacheAsyncDeleteDTO).getBytes());
        try{
            mqProducer.send(message);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 从redis缓存根据userId查询用户标签
     * @param userId 用户id
     * @return
     */
    private UserTagDTO queryByUserIdFromRedis(Long userId){
        String redisKey = cacheKeyBuilder.buildTagKey(userId);
        UserTagDTO userTagDTO = redisTemplate.opsForValue().get(redisKey);
        if(userTagDTO != null){
            return userTagDTO;
        }
        //缓存为空，mysql查询对象，并加入缓存
        UserTagPO userTagPO = userTagMapper.selectById(userId);
        if(userTagPO == null){
            return null;
        }
        userTagDTO = ConvertBeanUtils.convert(userTagPO, UserTagDTO.class);
        redisTemplate.opsForValue().set(redisKey, userTagDTO);
        redisTemplate.expire(redisKey,30, TimeUnit.MINUTES);
        return userTagDTO;
    }
}